#include "LIGHT_STD_Config.h"

#define SAMPLE_TIMES     16      // 采样次数（必须是2的幂，便于后续使用移位操作代替除法）
                                // 选择16次因为：1) 2^4=16 2) 提供较好的噪声抑制 3) 总采样时间合理(约32ms)

u16 Light_Value[1];             // ADC采样值存储数组（用于DMA传输）

/**
    ******************************************************************************
    * 函数名称：LIGHT_STD_Config
    * 功能描述：初始化光照传感器相关的硬件配置（GPIO、ADC和DMA）
    * 输入参数：无
    * 返回值  ：无
    * 详细说明：
    *   1. 配置DMA将ADC数据自动传输到内存
    *   2. 使能GPIOF和ADC3时钟
    *   3. 配置ADC时钟为PCLK2的4分频（确保ADC时钟不超过14MHz）
    *   4. 配置PF8为模拟输入模式（ADC输入必须配置为该模式）
    *   5. 初始化ADC3为独立模式、连续转换、右对齐
    *   6. 配置ADC3通道6（对应PF8）的采样时间为55.5个周期
    *   7. 使能ADC3的DMA请求
    *   8. 执行ADC校准流程（提高精度）
    *   9. 启动ADC连续转换
    * 
    * 硬件要求：
    *   - 光照传感器应连接到STM32的PF8引脚（对应ADC3通道6）
    *   - 需要DMA2控制器支持（用于自动传输ADC数据）
    * 
    * 调用示例：
    *   LIGHT_STD_Config(); // 初始化光照传感器硬件
    ******************************************************************************
**/
void LIGHT_STD_Config(void)
{
    // 配置DMA将ADC3数据寄存器(ADC3->DR)的值自动传输到Light_Value数组
    // 传输数据量为1（单通道采样），使用之前定义的DMA_Config函数
    DMA_Config((u32)&ADC3->DR, (u32)Light_Value, 1);

    // 使能GPIOF（PF8）和ADC3时钟（APB2总线）
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF | RCC_APB2Periph_ADC3, ENABLE);
    
    // 配置ADC时钟为PCLK2的4分频（假设PCLK2=72MHz，则ADC时钟=18MHz）
    // 注意：STM32F1系列ADC时钟最大14MHz，实际使用时需要调整分频系数
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);

    // 初始化GPIO结构体
    GPIO_InitTypeDef GPIO_InitStructure;
    // 配置PF8为模拟输入模式（ADC输入必须配置为该模式）
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;      // 模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;         // PF8引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 速度设置（对输入模式无影响）
    GPIO_Init(GPIOF, &GPIO_InitStructure);

    // 初始化ADC结构体
    ADC_InitTypeDef ADC_InitStructure;
    // ADC配置参数：
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    // 连续转换模式
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐（12位有效）
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;     // 独立ADC模式（非双ADC模式）
    ADC_InitStructure.ADC_NbrOfChannel = 1;                // 规则通道序列长度
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;          // 使能扫描模式（配合DMA使用）
    ADC_Init(ADC3, &ADC_InitStructure);
    
    // 配置ADC3的通道6（对应PF8）为规则通道1，采样时间55.5个周期
    // 较长采样时间可提高精度，适合高阻抗传感器
    ADC_RegularChannelConfig(ADC3, ADC_Channel_6, 1, ADC_SampleTime_55Cycles5);

    // 使能ADC3的DMA请求（ADC转换结果自动通过DMA传输）
    ADC_DMACmd(ADC3, ENABLE);
    
    // 使能ADC3
    ADC_Cmd(ADC3, ENABLE);

    // 禁用内部温度传感器和参考电压（降低噪声，提高光照测量精度）
    ADC_TempSensorVrefintCmd(DISABLE);

    // ADC校准流程（必须执行校准才能保证精度）
    ADC_ResetCalibration(ADC3);                  // 启动复位校准
    while(ADC_GetResetCalibrationStatus(ADC3));   // 等待复位校准完成
    ADC_StartCalibration(ADC3);                  // 启动校准
    while(ADC_GetCalibrationStatus(ADC3));       // 等待校准完成

    // 启动ADC连续转换（配合DMA实现自动采样）
    ADC_SoftwareStartConvCmd(ADC3, ENABLE);
}

/**
    ******************************************************************************
    * 函数名称：LIGHT_GetValue
    * 功能描述：获取单次光照强度AD采样值
    * 输入参数：无
    * 返回值  ：12位AD采样值（0-4095）
    * 详细说明：
    *   1. 使用软件触发单次ADC转换
    *   2. 等待转换完成（查询EOC标志）
    *   3. 读取转换结果并清除标志位
    *   4. 注意：此函数会阻塞直到转换完成
    * 
    * 硬件要求：
    *   - 需先调用LIGHT_STD_Config()初始化
    *   - 当使用DMA连续采样时，建议直接读取Light_Value数组
    * 
    * 调用示例：
    *   uint16_t light = LIGHT_GetValue(); // 获取当前光照值
    ******************************************************************************
**/
u16 LIGHT_GetValue(void)
{
    ADC_SoftwareStartConvCmd(ADC3, ENABLE);          // 软件触发ADC转换
    while(ADC_GetFlagStatus(ADC3, ADC_FLAG_EOC) == RESET); // 等待转换完成（阻塞）
    uint16_t value = ADC_GetConversionValue(ADC3);   // 读取转换结果（12位右对齐）
    ADC_ClearFlag(ADC3, ADC_FLAG_EOC);              // 清除转换完成标志
    return value;
}

/**
    ******************************************************************************
    * 函数名称：LIGHT_GetValue_HighPrecision
    * 功能描述：获取高精度光照强度值（均值滤波）
    * 输入参数：无
    * 返回值  ：12位AD采样值（0-4095）
    * 详细说明：
    *   1. 进行16次采样（SAMPLE_TIMES定义）
    *   2. 每次采样间隔2ms（合计约32ms）
    *      - 降低高频噪声影响
    *      - 避免传感器响应延迟
    *   3. 计算16次采样的平均值
    *   4. 使用右移4位代替除法提高效率（sum/16 = sum>>4）
    *   5. 使用32位累加器防止溢出（16*4095=65520 < 2^32）
    * 
    * 硬件要求：
    *   - 需先调用LIGHT_STD_Config()初始化
    *   - 需要Delay_ms()函数支持（实现精确延时）
    * 
    * 调用示例：
    *   uint16_t light = LIGHT_GetValue_HighPrecision(); // 获取高精度光照值
    ******************************************************************************
**/
u16 LIGHT_GetValue_HighPrecision(void)
{
    uint32_t sum = 0; // 32位累加器防止溢出（16*4095=65520 < 2^32）
    
    // 循环采样16次（SAMPLE_TIMES=16）
    for(uint8_t i=0; i<SAMPLE_TIMES; i++){
        sum += LIGHT_GetValue(); // 累加采样值
        Delay_ms(2);             // 每次采样间隔2ms（降低噪声影响）
    }
    
    // 返回平均值（sum/16），使用右移4位优化计算效率
    return (u16)(sum >> 4);  // 等价于sum/16但效率更高
}
